package com.java.example.network

import android.annotation.SuppressLint
import android.util.Base64
import android.util.Log
import com.google.gson.Gson
import com.google.gson.TypeAdapter
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody
import okio.Buffer
import retrofit2.Converter
import java.io.IOException
import java.io.OutputStreamWriter
import java.io.Writer
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.security.KeyFactory
import java.security.spec.X509EncodedKeySpec
import java.util.Random
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec

/**
 * @author chenll
 * @date 2024/2/22
 * Des：
 */
internal class CustomRequestBodyConverter<T>(private val gson: Gson, private val adapter: TypeAdapter<T>) : Converter<T, RequestBody> {
    @Throws(IOException::class)
    override fun convert(value: T): RequestBody {
        val buffer = Buffer()
        val writer: Writer = OutputStreamWriter(buffer.outputStream(), UTF_8)
        val jsonWriter = gson.newJsonWriter(writer)
        adapter.write(jsonWriter, value)
        jsonWriter.close()
        // 加密
        val data = buffer.readString(StandardCharsets.UTF_8)
        Log.d("加密前", "：$data")
        val encryptBody = encryption(data, random16())
        return if (null != encryptBody) {
            RequestBody.create(MEDIA_TYPE, gson.toJson(encryptBody))
        } else RequestBody.create(MEDIA_TYPE, buffer.readByteString())
        //
    }

    private fun random16(): String {
        val sb = StringBuilder()
        val random = Random()
        for (i in 0..15) {
            val nextInt = random.nextInt(CHAR_POOL.length - 1)
            sb.append(CHAR_POOL[nextInt])
        }
        return sb.toString()
    }

    @SuppressLint("GetInstance")
    private fun encryption(data: String, key: String): EncryptBody? {
        return try {
            val aes = Cipher.getInstance("AES/ECB/PKCS5PADDING")
            aes.init(Cipher.ENCRYPT_MODE, generateAESKey(key))
            val bytes_aes = aes.doFinal(data.toByteArray(StandardCharsets.UTF_8))
            val requestData = Base64.encodeToString(bytes_aes, Base64.DEFAULT)
            //
            val x509EncodedKeySpec = X509EncodedKeySpec(
                    Base64.decode(CustomConvertFactory.SERVER_PUBLIC_KEY, Base64.DEFAULT)
            )
            val keyFactory = KeyFactory.getInstance("RSA")
            val publicKey = keyFactory.generatePublic(x509EncodedKeySpec)
            val rsa = Cipher.getInstance("RSA/None/PKCS1Padding")
            rsa.init(Cipher.ENCRYPT_MODE, publicKey)
            val bytes_rsa = rsa.doFinal(key.toByteArray(StandardCharsets.UTF_8))
            val encrypted = Base64.encodeToString(bytes_rsa, Base64.DEFAULT)
            //
            val encryptBody = EncryptBody()
            encryptBody.requestData = requestData
            encryptBody.encrypted = encrypted
            encryptBody
        } catch (e: Exception) {
            null
        }
    }

    private fun generateAESKey(key: String): SecretKeySpec {
        return SecretKeySpec(key.toByteArray(StandardCharsets.UTF_8), "AES/ECB/PKCS5PADDING")
    }

    companion object {
        private val MEDIA_TYPE: MediaType = "application/json; charset=UTF-8".toMediaType()
        private val UTF_8 = Charset.forName("UTF-8")
        private const val CHAR_POOL = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678"
    }
}
